home *** CD-ROM | disk | FTP | other *** search
/ Shareware Super Platinum 8 / Shareware Super Platinum 8.iso / mac / PROGTOOL / GWMALLOC.ZIP;1 / GWMALLOC.TAR / gw_malloc / malloc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-08  |  11.5 KB  |  480 lines

  1. /*
  2.  * user-level memory-allocation routines
  3.  *
  4.  * Copyright 1992 by Gray Watson and the Antaire Corporation
  5.  *
  6.  * This file is part of the malloc-debug package.
  7.  *
  8.  * This library is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Library General Public
  10.  * License as published by the Free Software Foundation; either
  11.  * version 2 of the License, or (at your option) any later version.
  12.  *
  13.  * This library is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * Library General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Library General Public
  19.  * License along with this library (see COPYING-LIB); if not, write to the
  20.  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  *
  22.  * The author of the program may be contacted at gray.watson@antaire.com
  23.  */
  24.  
  25. /*
  26.  * This file contains the user-level calls to the memory allocation
  27.  * routines.  It handles a lot of the miscellaneous support garbage for
  28.  * chunk.c which is the real heap manager.
  29.  */
  30.  
  31. #define MALLOC_DEBUG_DISABLE
  32.  
  33. #include "malloc.h"
  34. #include "malloc_loc.h"
  35.  
  36. #include "chunk.h"
  37. #include "compat.h"
  38. #include "conf.h"
  39. #include "dbg_values.h"
  40. #include "error.h"
  41. #include "error_str.h"
  42. #include "error_val.h"
  43. #include "heap.h"
  44. #include "malloc_lp.h"
  45.  
  46. #if INCLUDE_RCS_IDS
  47. LOCAL    char    *rcs_id =
  48.   "$Id: malloc.c,v 1.22 1993/04/05 22:30:13 gray Exp $";
  49. #endif
  50.  
  51. /*
  52.  * exported variables
  53.  */
  54. /* logfile for dumping malloc info, MALLOC_LOGFILE env. var overrides this */
  55. EXPORT    char        *malloc_logpath    = NULL;
  56. /* internal malloc error number for reference purposes only */
  57. EXPORT    int        malloc_errno = 0;
  58.  
  59. /* local routines */
  60. LOCAL    int        malloc_startup(void);
  61. EXPORT    void        malloc_shutdown(void);
  62.  
  63. /* local variables */
  64. LOCAL    int        malloc_enabled    = FALSE; /* have we started yet? */
  65. LOCAL    char        in_alloc    = FALSE; /* can't be here twice */
  66. LOCAL    char        log_path[128]    = { NULLC }; /* storage for env path */
  67.  
  68. /* debug variables */
  69. LOCAL    char        *malloc_address    = NULL;    /* address to catch */
  70. LOCAL    int        address_count    = 0;    /* address argument */
  71. LOCAL    char        start_file[128] = { NULLC }; /* file to start at */
  72. LOCAL    int        start_line    = 0;    /* line in module to start */
  73. LOCAL    int        start_count    = -1;    /* start after X */
  74. LOCAL    int        check_interval    = -1;    /* check every X */
  75.  
  76. /****************************** local utilities ******************************/
  77.  
  78. /*
  79.  * hexadecimal STR to int translation
  80.  */
  81. LOCAL    int    hex_to_int(char * str)
  82. {
  83.   int        ret;
  84.   
  85.   /* strip off spaces */
  86.   for (; *str == ' ' || *str == '\t'; str++);
  87.   
  88.   /* skip a leading 0[xX] */
  89.   if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X'))
  90.     str += 2;
  91.   
  92.   for (ret = 0;; str++) {
  93.     if (*str >= '0' && *str <= '9')
  94.       ret = ret * 16 + (*str - '0');
  95.     else if (*str >= 'a' && *str <= 'f')
  96.       ret = ret * 16 + (*str - 'a' + 10);
  97.     else if (*str >= 'A' && *str <= 'F')
  98.       ret = ret * 16 + (*str - 'A' + 10);
  99.     else
  100.       break;
  101.   }
  102.   
  103.   return ret;
  104. }
  105.  
  106. /*
  107.  * a call to the alloc routines has been made, check the debug variables
  108.  * returns [NO]ERROR.
  109.  */
  110. LOCAL    int    check_debug_vars(const char * file, const int line)
  111. {
  112.   static int    iterc = 0;
  113.   
  114.   if (in_alloc) {
  115.     malloc_errno = MALLOC_IN_TWICE;
  116.     _malloc_perror("check_debug_vars");
  117.     /* malloc_perror may die already */
  118.     _malloc_die();
  119.     /*NOTREACHED*/
  120.   }
  121.   
  122.   in_alloc = TRUE;
  123.   
  124.   if (! malloc_enabled)
  125.     if (malloc_startup() != NOERROR)
  126.       return ERROR;
  127.   
  128.   /* check start file/line specifications */
  129.   if (! BIT_IS_SET(_malloc_debug, DEBUG_CHECK_HEAP)
  130.       && start_file[0] != NULLC
  131.       && file != NULL
  132.       && strcmp(start_file, file) == 0
  133.       && (line == 0 || line == start_line))
  134.     BIT_SET(_malloc_debug, DEBUG_CHECK_HEAP);
  135.   
  136.   /* start checking heap after X times */
  137.   if (start_count != -1 && --start_count == 0)
  138.     BIT_SET(_malloc_debug, DEBUG_CHECK_HEAP);
  139.   
  140.   /* checking heap every X times */
  141.   if (check_interval != -1) {
  142.     if (++iterc >= check_interval) {
  143.       BIT_SET(_malloc_debug, DEBUG_CHECK_HEAP);
  144.       iterc = 0;
  145.     }
  146.     else
  147.       BIT_CLEAR(_malloc_debug, DEBUG_CHECK_HEAP);
  148.   }
  149.   
  150.   /* after all that, do we need to check the heap? */
  151.   if (BIT_IS_SET(_malloc_debug, DEBUG_CHECK_HEAP))
  152.     (void)_chunk_heap_check();
  153.   
  154.   return NOERROR;
  155. }
  156.  
  157. /*
  158.  * check out a pointer to see if we were looking for it.
  159.  * may not return.
  160.  */
  161. LOCAL    void    check_var(const char * file, const int line, char * pnt)
  162. {
  163.   static int    addc = 0;
  164.   
  165.   if (malloc_address == NULL || pnt != malloc_address)
  166.     return;
  167.   
  168.   if (++addc < address_count)
  169.     return;
  170.   
  171.   if (BIT_IS_SET(_malloc_debug, DEBUG_LOG_BAD_POINTER))
  172.     _malloc_message("found address '%#lx' after %d pass%s from '%s:%u'",
  173.             pnt, addc, (addc == 1 ? "" : "es"), file, line);
  174.   malloc_errno = MALLOC_POINTER_FOUND;
  175.   _malloc_perror("check_var");
  176. }
  177.  
  178. /*
  179.  * get the values of malloc environ variables
  180.  */
  181. LOCAL    void    get_environ(void)
  182. {
  183.   char        *env;
  184.   
  185.   /* get the malloc_debug value */
  186.   env = (char *)getenv(DEBUG_ENVIRON);
  187.   if (env != NULL)
  188.     _malloc_debug = hex_to_int(env);
  189.   
  190.   /* get the malloc debug logfile name into a holding variable */
  191.   env = (char *)getenv(LOGFILE_ENVIRON);
  192.   if (env != NULL) {
  193.     (void)strcpy(log_path, env);
  194.     malloc_logpath = log_path;
  195.   }
  196.   
  197.   /* watch for a specific address and die when we get it */
  198.   env = (char *)getenv(ADDRESS_ENVIRON);
  199.   if (env != NULL) {
  200.     char    *addp;
  201.     
  202.     addp = index(env, ':');
  203.     if (addp != NULL) {
  204.       *addp = NULLC;
  205.       address_count = atoi(addp + 1);
  206.     }
  207.     else
  208.       address_count = 1;
  209.     
  210.     malloc_address = (char *)hex_to_int(env);
  211.   }
  212.   
  213.   /* check the heap every X times */
  214.   env = (char *)getenv(INTERVAL_ENVIRON);
  215.   if (env != NULL)
  216.     check_interval = atoi(env);
  217.   
  218.   /*
  219.    * start checking the heap after X iterations OR
  220.    * start at a file:line combination
  221.    */
  222.   env = (char *)getenv(START_ENVIRON);
  223.   if (env != NULL) {
  224.     char    *startp;
  225.     
  226.     BIT_CLEAR(_malloc_debug, DEBUG_CHECK_HEAP);
  227.     
  228.     startp = index(env, ':');
  229.     if (startp != NULL) {
  230.       *startp = NULLC;
  231.       (void)strcpy(start_file, env);
  232.       start_line = atoi(startp + 1);
  233.       start_count = 0;
  234.     }
  235.     else
  236.       start_count = atoi(env);
  237.   }
  238. }
  239.  
  240. /*************************** startup/shutdown calls **************************/
  241.  
  242. /*
  243.  * startup the memory-allocation module
  244.  */
  245. LOCAL    int    malloc_startup(void)
  246. {
  247.   /* have we started already? */
  248.   if (malloc_enabled)
  249.     return ERROR;
  250.   
  251.   /* set this here so if an error occurs below, it will not try again */
  252.   malloc_enabled = TRUE;
  253.   
  254.   /* get the environmental variables */
  255.   get_environ();
  256.   
  257.   /* startup heap code */
  258.   _heap_startup();
  259.   
  260.   /* startup the chunk lower-level code */
  261.   if (_chunk_startup() == ERROR)
  262.     return ERROR;
  263.   
  264.   return NOERROR;
  265. }
  266.  
  267. /*
  268.  * shutdown memory-allocation module, provide statistics if necessary
  269.  */
  270. EXPORT    void    malloc_shutdown(void)
  271. {
  272.   /* NOTE: do not test for IN_TWICE here */
  273.   
  274.   /* do we need to check the heap? */
  275.   if (BIT_IS_SET(_malloc_debug, DEBUG_CHECK_HEAP))
  276.     (void)_chunk_heap_check();
  277.   
  278.   /* dump some statistics to the logfile */
  279.   if (BIT_IS_SET(_malloc_debug, DEBUG_LOG_STATS))
  280.     _chunk_list_count();
  281.   if (BIT_IS_SET(_malloc_debug, DEBUG_LOG_STATS))
  282.     _chunk_stats();
  283.   
  284.   /* report on non-freed pointers */
  285.   if (BIT_IS_SET(_malloc_debug, DEBUG_LOG_NONFREE))
  286.     _chunk_dump_not_freed();
  287.   
  288.   /* NOTE: do not set malloc_enabled to false here */
  289. }
  290.  
  291. /******************************** memory calls *******************************/
  292.  
  293. /*
  294.  * allocate and return a SIZE block of bytes
  295.  */
  296. EXPORT    char    *malloc(unsigned int size)
  297. {
  298.   char        *newp;
  299.   
  300.   if (check_debug_vars(_malloc_file, _malloc_line) != NOERROR)
  301.     return MALLOC_ERROR;
  302.   
  303.   newp = (char *)_chunk_malloc(_malloc_file, _malloc_line, size);
  304.   check_var(_malloc_file, _malloc_line, newp);
  305.   
  306.   in_alloc = FALSE;
  307.   
  308.   return newp;
  309. }
  310.  
  311. /*
  312.  * allocate and return a block of bytes able to hold NUM_ELEMENTS of elements
  313.  * of SIZE bytes and zero the block
  314.  */
  315. EXPORT    char    *calloc(unsigned int num_elements, unsigned int size)
  316. {
  317.   char        *newp;
  318.   unsigned int    len = num_elements * size;
  319.   
  320.   if (check_debug_vars(_malloc_file, _malloc_line) != NOERROR)
  321.     return CALLOC_ERROR;
  322.   
  323.   /* needs to be done here */
  324.   _calloc_count++;
  325.   
  326.   /* alloc and watch for the die address */
  327.   newp = (char *)_chunk_malloc(_malloc_file, _malloc_line, len);
  328.   check_var(_malloc_file, _malloc_line, newp);
  329.   
  330.   (void)memset(newp, NULLC, len);
  331.   
  332.   in_alloc = FALSE;
  333.   
  334.   return newp;
  335. }
  336.  
  337. /*
  338.  * resizes OLD_PNT to SIZE bytes and return the new space after either copying
  339.  * all of OLD_PNT to the new area or truncating
  340.  */
  341. EXPORT    char    *realloc(char * old_pnt, unsigned int new_size)
  342. {
  343.   char        *newp;
  344.   
  345. #if ALLOW_REALLOC_NULL
  346.   if (old_pnt == NULL)
  347.     return malloc(new_size);
  348. #endif
  349.   
  350.   if (check_debug_vars(_malloc_file, _malloc_line) != NOERROR)
  351.     return REALLOC_ERROR;
  352.   
  353.   check_var(_malloc_file, _malloc_line, old_pnt);
  354.   newp = (char *)_chunk_realloc(_malloc_file, _malloc_line, old_pnt, new_size);
  355.   check_var(_malloc_file, _malloc_line, newp);
  356.   
  357.   in_alloc = FALSE;
  358.   
  359.   return newp;
  360. }
  361.  
  362. /*
  363.  * release PNT in the heap, returning FREE_[NO]ERROR
  364.  */
  365. EXPORT    int    free(char * pnt)
  366. {
  367.   int        ret;
  368.   
  369.   if (check_debug_vars(_malloc_file, _malloc_line) != NOERROR)
  370.     return FREE_ERROR;
  371.   
  372.   check_var(_malloc_file, _malloc_line, pnt);
  373.   ret = _chunk_free(_malloc_file, _malloc_line, pnt);
  374.   
  375.   in_alloc = FALSE;
  376.   
  377.   return ret;
  378. }
  379.  
  380. /******************************** utility calls ******************************/
  381.  
  382. /*
  383.  * call through to _heap_map function, returns [NO]ERROR
  384.  */
  385. EXPORT    int    malloc_heap_map(void)
  386. {
  387.   if (check_debug_vars(NULL, 0) != NOERROR)
  388.     return ERROR;
  389.   
  390.   _chunk_log_heap_map();
  391.   
  392.   in_alloc = FALSE;
  393.   
  394.   return NOERROR;
  395. }
  396.  
  397. /*
  398.  * verify pointer PNT, if PNT is 0 then check the entire heap.
  399.  * returns MALLOC_VERIFY_[NO]ERROR
  400.  */
  401. EXPORT    int    malloc_verify(char * pnt)
  402. {
  403.   int    ret;
  404.   
  405.   if (check_debug_vars(NULL, 0) != NOERROR)
  406.     return MALLOC_VERIFY_ERROR;
  407.   
  408.   if (pnt != 0)
  409.     ret = _chunk_pnt_check("malloc_verify", pnt, 0, 0);
  410.   else
  411.     ret = _chunk_heap_check();
  412.   
  413.   in_alloc = FALSE;
  414.   
  415.   if (ret == NOERROR)
  416.     return MALLOC_VERIFY_NOERROR;
  417.   else
  418.     return MALLOC_VERIFY_ERROR;
  419. }
  420.  
  421. /*
  422.  * set the global debug functionality flags to DEBUG (0 to disable).
  423.  * returns [NO]ERROR
  424.  */
  425. EXPORT    int    malloc_debug(int debug)
  426. {
  427.   int    hold;
  428.   
  429.   if (check_debug_vars(NULL, 0) != NOERROR)
  430.     return MALLOC_ERROR;
  431.   
  432.   /* make sure that the not-changeable flags' values are preserved */
  433.   hold = _malloc_debug & DEBUG_NOT_CHANGEABLE;
  434.   debug &= ~DEBUG_NOT_CHANGEABLE;
  435.   _malloc_debug = debug | hold;
  436.   
  437.   in_alloc = FALSE;
  438.   
  439.   return NOERROR;
  440. }
  441.  
  442. /*
  443.  * examine pointer PNT and returns SIZE, and FILE / LINE info on it
  444.  * if any of the pointers are not NULL.
  445.  * returns NOERROR or ERROR depending on whether PNT is good or not
  446.  */
  447. EXPORT    int    malloc_examine(char * pnt, unsigned int * size,
  448.                    char ** file, unsigned int * line)
  449. {
  450.   int        ret;
  451.   
  452.   if (check_debug_vars(NULL, 0) != NOERROR)
  453.     return ERROR;
  454.   
  455.   ret = _chunk_read_info(pnt, size, file, line);
  456.   
  457.   in_alloc = FALSE;
  458.   
  459.   if (ret == NOERROR)
  460.     return NOERROR;
  461.   else
  462.     return ERROR;
  463. }
  464.  
  465. /*
  466.  * malloc version of strerror to return the string version of ERRNUM
  467.  * returns the string for MALLOC_BAD_ERRNO if ERRNUM is out-of-range.
  468.  */
  469. EXPORT    char    *malloc_strerror(int errnum)
  470. {
  471.   /*
  472.    * NOTE: should not check_debug_vars here because _malloc_perror calls this.
  473.    */
  474.   
  475.   if (! IS_MALLOC_ERRNO(errnum))
  476.     return malloc_errlist[MALLOC_BAD_ERRNO];
  477.   else
  478.     return malloc_errlist[errnum];
  479. }
  480.